/*
 * ADCBoard.cpp
 *
 *  Created on: May 6, 2010
 *      Author: Mario Rodriguez Huizar
 */

#include "ADCBoard.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define CPLD_RUNNING 		0x01
#define CPLD_CLOCK 			32000000
#define CUSHION				16
#define MAX_QUEUE_LENGTH 	50
#define FIFO_SIZE			512
#define BOARD_SPACING		0x20   /* Boards @ 0x100, 0x120, 0x140, 0x160 */
#define IO_OFFSET			0x100
#define FIFO_COUNTER_MASK	6

#define BID_REG_OFFSET		0
#define CFG_REG_OFFSET		2/sizeof(uint16_t)
#define DLYMSB_REG_OFFSET	4/sizeof(uint16_t)
#define DLYLSB_REG_OFFSET	6/sizeof(uint16_t)
#define STATUS_REG_OFFSET	8/sizeof(uint16_t)
#define READFIFO_REG_OFFSET	0x0A/sizeof(uint16_t)
#define DAC_REG_BASE 		0x0E/sizeof(uint16_t)
#define DAC_REG_BASE8 		0x0E

#define JP4 0x80
#define JP3 0x40
#define JP2 0x20
#define JP1 0x10

#define writeRegister16(REGISTER, VALUE)	*(pMappedBaseAddr16+REGISTER) = VALUE
#define writeRegister8(REGISTER, VALUE)		*(pMappedBaseAddr8+REGISTER) = VALUE
#define readRegister16(REGISTER)			*(pMappedBaseAddr16+REGISTER)

typedef enum {
	TS_ADCINVALID = 0,
			TS_ADC16 = 0x3E,
			TS_ADC24
} adctype;

union ADCStatusRegister {
	struct {
		uint16_t intEn   	: 1;	//interrupt enabled flag
		uint16_t ffHeadChan : 5;	//channel on the head of the FIFO
		uint16_t ffCount	: 10;	//how many 16bit data is in the fifo
	} field;
	uint16_t ADCStatus;
};

const char *ADCBoard::tsadclib16ErrorText[]={
		"Can't openmem",
		"Can't mapio16",
		"Can't mapio8",
		"Not initialized",
		"Bad hardware",
		"Bad parameter",
		"Bad board",
		"Can't open IRQ6",
		"Can't open IRQ7",
		"FIFO Overrun"
};

ADCBoard::ADCBoard(uint32_t baseAddr16, uint32_t baseAddr8, uint8_t theBoardId)
: myBaseAddr8(baseAddr8), boardId(theBoardId)  {
	//Initialize our buffers
	for(uint8_t index=0; index<MAX_ADC_CHANNELS; index++ ){
		myADCPackets[index].length=0;
		//we dont initialize the data because it will be overwritten
	}
	initialized = false;   //board not yet initialized
	fifoOverflow = false;
	maxChannel = 0;
	IoBase8 = baseAddr8;   //needed to map the device into memory space
	IoBase16 = baseAddr16; //we store the needed offset for our devices
	tsadclib16_verboseFlag = true; //for debug TODO remove
	//TODO configure interrupts
}

int8_t ADCBoard::initADC(ADCQueueManager *pRxADCQueues){
#ifndef X86
	int memoryFd;
	uint8_t jumpers=0;
	uint8_t boardType=0;
#endif

	assert(pRxADCQueues); //the memory of ppRxADCQueues has to be already reserved
	// Initialize pointers to associated queue manager
	//we make this assignment here and not in the constructor, in the case that
	//this class were inside another class and the constructor of the other class
	//will execute first leaving us without memory.
	pADCQueues = pRxADCQueues;

#ifndef X86
	//  Set our Board address as a pointer into memory space.
	if ((memoryFd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
		return TSADCERR_CANTOPENMEM;

	pMappedBaseAddr16 = (uint16_t *) mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
			MAP_SHARED, memoryFd, IoBase16);
	if (pMappedBaseAddr16 == MAP_FAILED)
		return TSADCERR_CANTMAPIO16;

	pMappedBaseAddr8 = (uint8_t *) mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
			MAP_SHARED, memoryFd, IoBase8);
	if (pMappedBaseAddr8 == MAP_FAILED)
		return TSADCERR_CANTMAPIO8;

	// Scan for board
	pMappedBaseAddr16 += IO_OFFSET/sizeof(uint16_t);
	pMappedBaseAddr8 += IO_OFFSET;
	boardType = *pMappedBaseAddr8; //read from mapped device register the boardtype
	jumpers = *(pMappedBaseAddr8+1); //read from mapped device register the jumper position

	if (tsadclib16_verboseFlag)
		fprintf( stderr,
				"tsadclib16_init Io16:%p Io8:%p ID:0x%02x\n",
				pMappedBaseAddr16, pMappedBaseAddr8, boardType);

	if (boardType == TS_ADC16) {
		if (tsadclib16_verboseFlag)
			fprintf(stderr, "jumpers: (0x%02x) %s %s %s %s (%s)\n",
					jumpers, jumpers & JP1 ? "JP1" : "",
							jumpers & JP2 ? "JP2" : "", jumpers & JP3 ? "JP3" : "",
									jumpers & JP4 ? "JP4" : "", (jumpers & JP4) ? "IRQ7"
											: "IRQ6");

		if (!(jumpers & JP3)) { // JP3 must be ON for ARM
			if (tsadclib16_verboseFlag)
				fprintf(stderr, "JP3 is NOT set and is required for ARM\n");
			return TSADCERR_HARDWARE;
		}

		initialized = true;
		if (tsadclib16_verboseFlag)
			fprintf(stderr, "ADC16 found\n");
	}
#else //we are native compiling
	initialized = true;
#endif
	return 0;
}

/*
 *  configure our board in order to acquire numberOfSamples samples from all
 *  channels on ADC theBoard from zero through maxChannel in range range at
 *  rate sampleRate
 */
void ADCBoard::config(int16_t theMaxChannel, int32_t sampleRate, adcrange range) {
#ifndef X86
	unsigned int pacing; //24-bit pacing clock=0 (Reg.ADCDLY) indicates continuous ADC scanning;
	//24-bit pacing clock!=0 indicates the amount of 32MHz clocks the system will wait before
	//the next scan cycle starts;
	uint16_t adccfg; //configure register for the ADC. For more info see TS-ADC16 Getting started sheet
	uint16_t numChan; //NUMCHAN should contain the number of pair of channels
	//(one from each chip) to be read in a single scan cycle, starting by channel 0
	//up to 7 (TS-ADC16) (see TS-ADC16 Getting started sheet)
#else
	sampleRate = 0; //dummy instructions to remove warnings when native compiling
	range = ADCRANGE_55S;
#endif

	assert(initialized); //board initialized?
	assert(maxChannel<=MAX_ADC_CHANNELS);
	maxChannel=theMaxChannel; //maxChannel should only be modified here!
#ifndef X86
	*(pMappedBaseAddr16+CFG_REG_OFFSET) &= ~CPLD_RUNNING; //stop ADC
	//Flush the FIFO to assure a clean start
	while (readRegister16(STATUS_REG_OFFSET)>>FIFO_COUNTER_MASK)    // read the fifo counter on the status register
		readRegister16(READFIFO_REG_OFFSET);         // wait until fifo is empty

	numChan = (uint16_t) ((maxChannel & ~1) >> 1); //calculate the maximal number of pair channels we want to sample

	//the bits 4-1 of adccfg should contain NUMCHAN
	adccfg = range | (uint16_t) (numChan << 1); //we configure the input range i.e +-5V or +-10v and the the channels to be read
	writeRegister16(CFG_REG_OFFSET, CPLD_RUNNING); //we write to the register of the ADC which is mapped to memory
	writeRegister16(CFG_REG_OFFSET, adccfg);  //configure ADC, we also stop the ADC

	while (readRegister16(STATUS_REG_OFFSET)>>FIFO_COUNTER_MASK) 	// read the fifo counter on the status register
		readRegister16(READFIFO_REG_OFFSET);   // wait until fifo is empty
	pacing = CPLD_CLOCK / sampleRate;
	if (pacing < 0x140)
		pacing = 0;
	//fprintf(stderr, "sample - pacing:%d numChan:%d samples:%d AdcCfg:%03x\n", pacing, numChan, currentAdcBoard->sampleCnt, adccfg);
	writeRegister16(DLYLSB_REG_OFFSET,pacing & 0xffff);
	writeRegister16(DLYMSB_REG_OFFSET,pacing >> 16); //pacing has only 24bit
	writeRegister16(CFG_REG_OFFSET,adccfg); //Updating the pacing clock value requires stopping the system by writing 0 to Reg.ADCCFG.SYSCOM;

	if (tsadclib16_verboseFlag) {
		fprintf(stderr, "pacing=0x%x\n", pacing);
		fprintf(stderr, "adccfg=0x%x\n", readRegister16(CFG_REG_OFFSET));
	}

	writeRegister16(CFG_REG_OFFSET,adccfg | CPLD_RUNNING); //  Start scanning
#endif
}

// Invoked when the user asks for a read to the FIFO, this has to be done
// continuously or the FIFO will overflow. The data will be saved on the packets
// when a complete sample is filled with data (this means there is at least one adcpacket
// in each queue) it returns TSADCSTATUS_DATA
// 0 means no data
int8_t ADCBoard::readFifo(){
	int8_t returnValue=TSADCSTATUS_NODATA;
#ifndef X86
	ADCStatusRegister statusRegister;
	uint16_t fifoCounter;
	uint16_t  currentPacketLenght;
	int8_t tempReturnValue;


	statusRegister.ADCStatus = readRegister16(STATUS_REG_OFFSET);
	fifoCounter = statusRegister.field.ffCount;

	//printf("fifoCounter %d: \n", fifoCounter);
	if(fifoCounter>=FIFO_SIZE){ //If FIFO is full (Reg.ADCSTAT.FFFULL), then system status changes to stopped
		// scan cycles are stopped and system waits until host restarts it via bit (Reg.ADCCFG.SYSCOM).
		// The remaining data in the FIFO is available until the system is restarted;
		fprintf(stderr, "ADC error: FIFO Overrrun\n");
		returnValue = TSADCERR_FIFOOVERRUN;
		fifoOverflow = true;
	}

	if (fifoCounter > 0) { //there's some data to read!
		while(fifoCounter>0){
			//TODO make a Mask with ADCStatus
			statusRegister.ADCStatus = readRegister16(STATUS_REG_OFFSET);
			uint8_t channel = statusRegister.field.ffHeadChan; //sync channel with ADC internal channel
			//printf("channel %d: \n", channel);
			currentPacketLenght = myADCPackets[channel].length; // read the packet length of the current channel
			if(currentPacketLenght == ADC_PACKET_SIZE){
				// the packet is complete, transfer it to the queue
				tempReturnValue = packetReceiveComplete(channel);
				if(tempReturnValue!=SUCCESS){ //only if the packetReceiveComplete gets an error
					returnValue = tempReturnValue; //we update the returnValue
				}
				if( pADCQueues->getTotalLength() > maxChannel ){ //we have in our queues at least 1 adcpacket from each channel
					returnValue=TSADCSTATUS_DATA; //tell the higher level we have a complete sample
				}
				//printf("total length %d \n", pADCQueues->getTotalLength());
			}else{  //packet not yet full
				// read the data on the fifo and save it on the corresponding packet
				myADCPackets[channel].adcData[currentPacketLenght] = // retrieves ADC data out from the internal FIFO
						readRegister16(READFIFO_REG_OFFSET);  // and update internal FIFO counters and pointers
				myADCPackets[channel].length++;
				fifoCounter--;
			}
		}
	} else { //fifoCounter == 0
		if(fifoOverflow){
			printf("restarting FIFO due to overflow\n");
			*(pMappedBaseAddr16+CFG_REG_OFFSET)|=CPLD_RUNNING;
			fifoOverflow = false;
		}
	}
#else //simulate that we receive data from virtual ADC
	int maxChanNorm;
	if(maxChannel%2==0){
		maxChanNorm = maxChannel+1;
	} else {
		maxChanNorm = maxChannel;
	}
	for(int index=0; index<=maxChanNorm; index++) //for each channel
	{
		for(int index2=0; index2<ADC_PACKET_SIZE; index2++)  //fill packet of channel with simulation data
			myADCPackets[index].adcData[index2]=index2*100;
	}
	usleep(500000);

	for(int channel=0; channel<=maxChanNorm; channel++) //for each channel
	{//fill our queues with previous created packets
		ADCPacket *pNewAdcPacket = new ADCPacket;
		memcpy(pNewAdcPacket->adcData, myADCPackets[channel].adcData,
				ADC_PACKET_SIZE*sizeof(myADCPackets[channel].adcData[0]));
		// Copy the length
		pNewAdcPacket->length = ADC_PACKET_SIZE;

		//insert our ADCPacket to the Queue, Pass the message to the next layer
		if(pADCQueues->pushADCpacket(channel, pNewAdcPacket)){
			//success storing the packet in the queue
			//printf("pushPacket\n");
		}
		else{
			printf("Queue Overflow on Channel:%d from ADC \n", channel );
			returnValue = TSADCERR_QUEUEOVERFLOW;
		}
	}
	if( pADCQueues->getTotalLength() > maxChanNorm ){ //we have in our queues at least 1 adcpacket from each channel
		returnValue=TSADCSTATUS_DATA; //tell the higher level we have a complete sample
	}

#endif
	return returnValue;
}

// Called when we received a complete packet (length = ADC_PACKET_SIZE)
// If success returns 0, it can detect if the QUEUE length was exceeded
int8_t ADCBoard::packetReceiveComplete(uint8_t channel){
	int8_t returnValue=SUCCESS;
	assert(channel <= 15);
	// Allocate an ADC packet buffer for the message and copy the contents
	// from the receive buffer
	ADCPacket *pNewAdcPacket = new ADCPacket;
	assert(pNewAdcPacket);
	assert(myADCPackets[channel].length=ADC_PACKET_SIZE);
	memcpy(pNewAdcPacket->adcData, myADCPackets[channel].adcData,
			myADCPackets[channel].length*sizeof(myADCPackets[channel].adcData[0]));
	// Copy the length
	pNewAdcPacket->length = myADCPackets[channel].length;
	//insert our ADCPacket to the Queue, Pass the message to the next layer
	if(pADCQueues->pushADCpacket(channel, pNewAdcPacket)){
		//success storing the packet in the queue
	}
	else{
		printf("Queue Overflow on Channel:%d from ADC \n", channel );
		returnValue = TSADCERR_QUEUEOVERFLOW;
	}
	// Cleanup the message buffer for receiving the next message
	myADCPackets[channel].length = 0;
	return returnValue;
}

//For controlling the 4 channels of the 12-bit DAC AD5327
void ADCBoard::setDACvalue(uint8_t theDacChannel, dacrefrange theRefRange, uint16_t theValue){
	assert(initialized);
	assert(theDacChannel<4);
	assert(theValue<=4095); //maximal 12 bit value
	switch (theRefRange)
	{
	case DACRANGE_0VU:
	case DACRANGE_0VB:
	case DACRANGE_02VU:
	case DACRANGE_02VB:
		break;
	default:
		assert(false); //if we fall here the refrange was not correct
	}
	/*AD5327 Input shift Register Contents*/
	/*|A1|A0|GAIN|BUF|D11|D10|D9|D8|D7|D6|D5|D4|D3|D2|D1|D0|*/
	//A1 and A0 determines whether the data is for DAC A,B,C or D
	//GAIN controls the output range (0V to Vref or 0V to 2*Vref)
	//BUF controls whether reference of the addressed DAC is buffered or unbuffered
	//when buffered: high impedance to the voltage source driving
	//when unbuffered: user can have a reference as low as 0.25V and as high as Vdd
	theValue |= (((uint16_t) theDacChannel) << 14);
	theValue |= theRefRange;

#ifndef X86
	//Writing to DAC command register (Reg.DACCMD) using 16-bit writing cycles
	//will save the value of the specified DAC channel (Reg.DACCMD[15:14]) to an
	//internal register, but will not update the DAC channel
	writeRegister16(DAC_REG_BASE, theValue);
	//A DAC channel is only updated when there is a 8-bit write cycle to the
	//MSB DAC command register (REG.DACCMD+1 or BASE+0F);
	writeRegister8(DAC_REG_BASE8+1, theValue >> 8);
#endif
}

const char *ADCBoard::tsadclib1624_errors(int errorCode){
	if (errorCode <= TSADCERR_MAXERROR)
		return "";
	else
		return tsadclib16ErrorText[abs(errorCode) - 1];
}

ADCBoard::~ADCBoard(){

}
